package com.zwcl.common.core.utils;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.xmlbeans.impl.xb.xsdschema.Public;
import org.springframework.beans.FatalBeanException;
import org.springframework.cglib.beans.BeanMap;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;

import javax.xml.transform.Source;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.Map.Entry;

/**
 * @author: zepeng.lin
 * bean相关操作
 **/
public class BeanUtils {

    public static <Source, Target> Target convert(Source source, Class<Target> clazz) {
        return convert(source, clazz, null);
    }

    public static <Source, Target> List<Target> convertList(List<Source> sourceList, Class<Target> clazz) {
        return convertList(sourceList, clazz, null);
    }

    public static <Source, Target> Page<Target> convertPageResult(Page<Source> sourcePagedResult, Class<Target> clazz) {
        return convertPageResult(sourcePagedResult, clazz, null);
    }

    public static <Source, Target> Target convert(Source source, Class<Target> clazz,
                                                  FieldStuffer<Target> fieldStuffer) {
        Target target = org.springframework.beans.BeanUtils.instantiateClass(clazz);
        org.springframework.beans.BeanUtils.copyProperties(source, target);
        if (fieldStuffer != null) {
            fieldStuffer.fill(target);
        }
        return target;
    }

    public static <Source, Target> List<Target> convertList(List<Source> sourceList, Class<Target> clazz,
                                                            FieldStuffer<Target> fieldStuffer) {
        List<Target> targetList = new ArrayList<Target>();
        for (Source source : sourceList) {
            targetList.add(convert(source, clazz, fieldStuffer));
        }
        return targetList;
    }

    public static <Source, Target> Page<Target> convertPageResult(Page<Source> sourcePageResult,
                                                                  Class<Target> clazz, FieldStuffer<Target> fieldStuffer) {
        Page<Target> newPages= new Page<Target>(sourcePageResult.getCurrent(),sourcePageResult.getSize());
        newPages.setTotal(sourcePageResult.getTotal());
        newPages.setRecords(convertList(sourcePageResult.getRecords(), clazz, fieldStuffer));
        return newPages;
    }

    public static <Source> Map<String, Object> convertMap(Source source) {
        Map<String, Object> map = new HashMap<String, Object>();
        Map<?, ?> beanMap = BeanMap.create(source);
        for (Entry<?, ?> entry : beanMap.entrySet()) {
            map.put(entry.getKey().toString(), entry.getValue());
        }
        return map;
    }

    /**
     * 分页函数
     * @param currentPage   当前页数
     * @param pageSize  每一页的数据条数
     * @param list  要进行分页的数据列表
     * @return  当前页要展示的数据
     */
    public static <Source> Page<Source> getCurPage(Integer currentPage, Integer pageSize, List<Source> list) {
        Page<Source> curPage=new Page<>(currentPage,pageSize);
        int size = list.size();
        if(pageSize > size) {
            pageSize = size;
        }
        // 求出最大页数，防止currentPage越界
        int maxPage = size % pageSize == 0 ? size / pageSize : size / pageSize + 1;
        if(currentPage > maxPage) {
            currentPage = maxPage;
        }
        List<Source> pageList = new ArrayList();
        // 当前页第一条数据的下标
        int curIdx = currentPage > 1 ? (currentPage - 1) * pageSize : 0;
        if (curIdx + pageSize > size)
            pageList= list.subList(curIdx, size);
        else
            pageList= list.subList(curIdx, curIdx + pageSize);
//        // 将当前页的数据放进pageList
//        for(int i = 0; i < pageSize && curIdx + i < size; i++) {
//            pageList.add(list.get(curIdx + i));
//        }
        curPage.setCurrent(currentPage).setSize(pageSize).setTotal(list.size()).setPages(maxPage).setRecords(pageList);
        return curPage;
    }

    /***
     * 拷贝属性，null值不拷贝
     */
    public static <Source, Target> Target copyProperties(Source source, Target target, String... ignoreProperties) {
        Assert.notNull(source, "Source must not be null");
        Assert.notNull(target, "Target must not be null");

        Class<?> actualEditable = target.getClass();
        PropertyDescriptor[] targetPds = org.springframework.beans.BeanUtils.getPropertyDescriptors(actualEditable);
        List<String> ignoreList = (ignoreProperties != null ? Arrays.asList(ignoreProperties) : null);

        for (PropertyDescriptor targetPd : targetPds) {
            Method writeMethod = targetPd.getWriteMethod();
            if (writeMethod != null && (ignoreList == null || !ignoreList.contains(targetPd.getName()))) {
                PropertyDescriptor sourcePd = org.springframework.beans.BeanUtils
                        .getPropertyDescriptor(source.getClass(), targetPd.getName());
                if (sourcePd != null) {
                    Method readMethod = sourcePd.getReadMethod();
                    if (readMethod != null && ClassUtils.isAssignable(writeMethod.getParameterTypes()[0],
                            readMethod.getReturnType())) {
                        try {
                            if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
                                readMethod.setAccessible(true);
                            }
                            Object value = readMethod.invoke(source);
                            if (value != null) {
                                if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
                                    writeMethod.setAccessible(true);
                                }
                                writeMethod.invoke(target, value);
                            }
                        } catch (Throwable ex) {
                            throw new FatalBeanException(
                                    "Could not copy property '" + targetPd.getName() + "' from source to target", ex);
                        }
                    }
                }
            }
        }
        return target;
    }




}
